home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / opengl / xlib / dnbogl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  36.7 KB  |  977 lines

  1. /*
  2.  * Copyright 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.  * dnbogl.c :  an openGL-Xlib dial-and-button box program imitating the
  19.  *             behavior of the Dial & Button Confidence Test accessible
  20.  *             via the System Manager toolchest.  .  This is the "after"
  21.  *             version, ported from its "before" counterpart, located at
  22.  *             ../../GLX/dials+buttons/dnbglx.c
  23.  *
  24.  *   This program creates 2 openGL visuals placed side-by-side: 
  25.  *    1. a colormap, singlebuffer'd openGL window is used to draw the 
  26.  *       buttons and text area (where animation is not necessary), and
  27.  *    2. a colormap, doublebuffer'd openGL window draws the dials 
  28.  *       (smooth animation *is* needed here).
  29.  *
  30.  *   There are 2 functions prior to the infinite loop worth noting:
  31.  *
  32.  *    1. openwindow() sets up the X parent (including telling the WM what
  33.  *       attributes and constraints we'd like to have), the two openGL 
  34.  *       children, specifies what events each window is interested in,
  35.  *       loads the correct colormaps and then maps all three windows
  36.  *       (they will not become visible however until the first expose
  37.  *       events are generated/processed).
  38.  *
  39.  *    2. setupdevs() finds, opens, and creates a handle to the 
  40.  *       "dial+buttons" device structure.  it then determines the given 
  41.  *       type and class of each device event we're going to be interested 
  42.  *       in, and then makes requests to the server to send us events that 
  43.  *       match the events and devices described by the event list *and*
  44.  *       that come from our specific window.
  45.  *
  46.  *    NOTE:  the functionality of the GL routine SETVALUATOR(3G) (assign 
  47.  *           an initial value *and* range to a valuator) is not currently 
  48.  *           implemented in any X input extension from MIT.  Hence, for
  49.  *           the time being in this mixed-model imitation of the pure GL
  50.  *           dial and buttons confidence test, the dial valuator's are 
  51.  *           NOT constrained in their range to only [0..1023].  This is
  52.  *           the only known discrepancy between this program and the
  53.  *           original.  (There is a proposal to add this extension 
  54.  *           currently being debated but it is not finalized yet.)
  55.  *
  56.  *   Following this, the get/process input infinite loop occupies the
  57.  *    rest of the program's energies.  the core of this is the "default"
  58.  *    portion of the "switch (event.type)" statement which catches the
  59.  *    dial and button events being generated.  notice this is where the
  60.  *    {DnB_motion_type, DnB_press_type, DnB_release_type} vars come into 
  61.  *    play:  recall these were defined in setupdevs() with the 3 macros 
  62.  *    DeviceMotionNotify, DeviceButtonPress, and DeviceButtonRelease,
  63.  *    respectively.  XSelectExtensionEvent then was used to ask the
  64.  *    server to send us any events generated by these devices in our
  65.  *    window.
  66.  *
  67.  *   X window functionality worth noting:
  68.  *    dnbogl implements the funtionality in its window handling of creating 
  69.  *    a window the user is not allowed/able to resize utilizing the mwm 
  70.  *    MotifWindowHints structure.  this kind of control of what the window
  71.  *    can or can't be made to do can be useful at times.  
  72.  *    Also this program automatically places the window at a specific 
  73.  *    location on the console rather than letting the user interactively
  74.  *    plant it upon startup.
  75.  *    Both sets of functionality are defined in openwindow().
  76.  *
  77.  *   IrisGL Functionality missing in OpenGL:
  78.  *    the older dial and button box commands, dbtext() and setdblights(),
  79.  *    are not supported in openGL so they are missing from this port.
  80.  *
  81.  *                                   ratmandu -- ported to openGL, aug 93
  82.  */
  83. #include <stdio.h>
  84. #include <stdlib.h>
  85. #include <GL/gl.h>
  86. #include <GL/glu.h>
  87. #include <GL/glx.h>
  88. #include <X11/Xlib.h>
  89. #include <X11/Xutil.h>
  90. #include <X11/Xos.h>
  91. #include <X11/Xatom.h>
  92. #include <X11/extensions/XI.h>
  93. #include <X11/extensions/XInput.h>
  94.  
  95. #define xsize 840                    /* window's "unresize-able" dimensions */
  96. #define ysize 480
  97.  
  98. #define NumButtons 32
  99. #define NumDials    8
  100.  
  101. #define SWITCH_OFF  0                                      /* button states */
  102. #define SWITCH_ON   1
  103.  
  104. #define top     0                                    /* the X parent window */
  105. #define SBglwin 1        /* the "buttons" are drawn in a singlebuffered win */
  106. #define DBglwin 2        /* and the "dials" are drawn in a doublebuffer one */
  107. #define WINMAX 3
  108.  
  109. #define BLACK 0                      /* "stand-ins" for GL colormap indices */
  110. #define RED   1
  111. #define BLUE  4
  112. #define WHITE 7
  113.  
  114. typedef struct {          /* struct for each button's bottom-left origin    */
  115.     float xpos;           /* (i.e. position), and whether its being pressed */
  116.     float ypos;
  117.     int   state;
  118. } DB_BUTTON;
  119.  
  120. DB_BUTTON but[] = {         /* button positions & [initialized] state array */
  121.     11.0, 31.0, SWITCH_OFF,
  122.     16.0, 31.0, SWITCH_OFF,
  123.     21.0, 31.0, SWITCH_OFF,
  124.     26.0, 31.0, SWITCH_OFF,
  125.     6.0,  26.0, SWITCH_OFF,
  126.     11.0, 26.0, SWITCH_OFF,
  127.     16.0, 26.0, SWITCH_OFF,
  128.     21.0, 26.0, SWITCH_OFF,
  129.     26.0, 26.0, SWITCH_OFF,
  130.     31.0, 26.0, SWITCH_OFF,
  131.     6.0,  21.0, SWITCH_OFF,
  132.     11.0, 21.0, SWITCH_OFF,
  133.     16.0, 21.0, SWITCH_OFF,
  134.     21.0, 21.0, SWITCH_OFF,
  135.     26.0, 21.0, SWITCH_OFF,
  136.     31.0, 21.0, SWITCH_OFF,
  137.     6.0,  16.0, SWITCH_OFF,
  138.     11.0, 16.0, SWITCH_OFF,
  139.     16.0, 16.0, SWITCH_OFF,
  140.     21.0, 16.0, SWITCH_OFF,
  141.     26.0, 16.0, SWITCH_OFF,
  142.     31.0, 16.0, SWITCH_OFF,
  143.     6.0,  11.0, SWITCH_OFF,
  144.     11.0, 11.0, SWITCH_OFF,
  145.     16.0, 11.0, SWITCH_OFF,
  146.     21.0, 11.0, SWITCH_OFF,
  147.     26.0, 11.0, SWITCH_OFF,
  148.     31.0, 11.0, SWITCH_OFF,
  149.     11.0, 6.0,  SWITCH_OFF,
  150.     16.0, 6.0,  SWITCH_OFF,
  151.     21.0, 6.0,  SWITCH_OFF,
  152.     26.0, 6.0,  SWITCH_OFF,
  153. };
  154.  
  155. typedef struct {                 /* struct for each dial's center point and */
  156.     float xcntr;                 /* current angle value (0 is at 12 o'clock)*/
  157.     float ycntr;
  158.     int   angle;
  159. } DB_DIAL;
  160.  
  161. DB_DIAL dia[] = {              /* dial positions & initialized values array */
  162.     10.0, 8.0,  0,
  163.     20.0, 8.0,  0,
  164.     10.0, 15.0, 0,
  165.     20.0, 15.0, 0,
  166.     10.0, 22.0, 0,
  167.     20.0, 22.0, 0,
  168.     10.0, 29.0, 0,
  169.     20.0, 29.0, 0,
  170. };
  171.  
  172.       /* function declarations */
  173. static void openwindow(char *);
  174. static void initGL(void);
  175. static void setupdevs(int *, int *, int *);
  176. static void makeSBwin(void);
  177. static void makeDBwin(void);
  178. void Winset(int);
  179. static void draw_dial(int);
  180. static void draw_button(int, int);
  181. static void makeRasterFont(Display **dpy);
  182. static void printString(char *s);
  183. static void clean_exit();
  184.  
  185. static Display *dpy;                               /* X server connection   */
  186. static Atom del_atom;                              /* WM_DELETE_WINDOW atom */
  187. static Window glwins[WINMAX];       /* holds X parent & 2 GL window handles */
  188. GLXContext glcontexts[WINMAX];        /* array for SB/DB window context ids */
  189.  
  190.       /* dial and buttons device event handles */
  191. int DnB_device_id;                               /* device handles for the  */
  192. int DnB_press_type;                              /* DnB id, and the press,  */
  193. int DnB_release_type;                            /* release, & motion       */
  194. int DnB_motion_type;                             /* events they'll generate */
  195.  
  196. short firstdialtouched = GL_FALSE;     /* used so text for dial valuators   */
  197.                                        /* doesn't get drawn until the dials */
  198.                                        /* actually are turned               */
  199.  
  200. int myExpose;                           /* needs to be global for makeDBwin */
  201. XEvent event;                           /* needs to be global for XIfEvent()*/
  202. int dialaxis[NumDials];     /* tells which dial(s) is(are) currently active */
  203. GLUquadricObj *qobj;                               /* for drawing the dials */
  204.  
  205. main(int argc, char *argv[])
  206. {
  207.     int myConfigure, myMotion, 
  208.         myButtPress, myButtRelease;             /* track which events occur */
  209.     int buttP[NumButtons], buttR[NumButtons];   /* track which buttons are  */
  210.                             /* pressed or released      */
  211.     int i;
  212.     int windowvisible;         /* tells event handler whether or not window */
  213.                    /* is iconified--when it is, we don't want   */
  214.                                /* to process any incoming events            */
  215.  
  216.  
  217.  
  218.     myExpose = myConfigure = myMotion = myButtPress = myButtRelease = GL_FALSE;
  219.  
  220.     openwindow(argv[0]);                    /* configure/open up the window */
  221.     initGL();
  222.  
  223.     setupdevs(buttP, buttR, dialaxis);  /* set up the dial and button boxes */
  224.     makeRasterFont(&dpy);                                      /* make font */
  225.  
  226.  
  227.    /* 
  228.     * The event loop.
  229.     */
  230.     while (1) {          /* standard logic:  get event(s), process event(s) */
  231.  
  232.         int axis_data[6];
  233.  
  234.         glFlush();                            /* For proper DGL performance */
  235.  
  236.        /* this "do while" loop does the `get events' half of the "get events,
  237.         *  process events" action of the infinite while.  this is to ensure
  238.         *  the event queue is always drained before the events that have come
  239.         *  in are processed.
  240.         */
  241.         do {
  242.  
  243.             XNextEvent(dpy, &event);
  244.             switch (event.type) {
  245.  
  246.             /* "Expose" events are sort of like "REDRAW" in gl-speak in
  247.              *  terms of when a window becomes visible, or a previously
  248.              *  invisible part becomes visible.
  249.              */
  250.                 case Expose:                        
  251.                 /* see if either or both of the GL windows needs redrawing.
  252.          *  this way if only one of the two is generating the event,
  253.          *  only it will get repainted.
  254.          */
  255.                     for (i=1; i<WINMAX; i++)
  256.                         if (event.xexpose.window == glwins[i])
  257.                                 myExpose |= (1 << (i-1));
  258.                     break;
  259.  
  260.  
  261.             /* "ConfigNotify" events are like "REDRAW" in terms of changes
  262.              *   to a window's size or position.  Since this prog locks 
  263.              *   the window's size, ConfigureNotify will occur only when
  264.              *   the window gets moved.
  265.              */
  266.                 case ConfigureNotify:  
  267.                     myConfigure = GL_TRUE;
  268.                     break;
  269.  
  270.  
  271.             /* "ButtonRelease" detects when LEFTMOUSE (Button1) is being
  272.              *  pressed indicating its time to leave.
  273.              */
  274.                 case ButtonRelease:
  275.                     if (event.xbutton.button == Button1)
  276.                         clean_exit();
  277.                     break;
  278.  
  279.  
  280.             /* "ClientMessage" is generated if the WM itself is being 
  281.              *  gunned down and sends an exit signal to any running prog.
  282.              */
  283.                 case ClientMessage:
  284.                     if (event.xclient.data.l[0] == del_atom)
  285.                         clean_exit();
  286.                     break;
  287.  
  288.  
  289.             /* "MapNotify" is generated when the window is made visible.
  290.              */
  291.                 case MapNotify:
  292.                     windowvisible = GL_TRUE;
  293.                     break;
  294.  
  295.  
  296.             /* "UnmapNotify" is generated when the window is made "invisible"
  297.              *  --like when the window is iconified.  When the window is
  298.              *  not visible (i.e. iconified), we don't want any events to
  299.              *  be processed.
  300.              */
  301.                 case UnmapNotify:
  302.                     windowvisible = GL_FALSE;
  303.                     break;
  304.  
  305.  
  306.             /* since interest is on the dNb's, they become the default.
  307.              */
  308.                 default:
  309.                 
  310.                     if (windowvisible) {    /* only process events if win is */
  311.                                           /* visible (not if it's iconified) */
  312.  
  313.                         if (event.type == DnB_motion_type) {  /* dial turned */
  314.  
  315.                             XDeviceMotionEvent *M=(XDeviceMotionEvent *)&event;
  316.  
  317.                             if (M->deviceid == DnB_device_id) {
  318.                                 i = M->first_axis;    /* save out which dial */
  319.                                 dialaxis[i]  = GL_TRUE;/*mark current "axis" */
  320.                                 dia[i].angle = M->axis_data[0];/* save angle */
  321.                                 myMotion = GL_TRUE;       /* set motion flag */
  322.                             } else
  323.                                 fprintf(stderr,"cable has been disconnected\n");
  324.  
  325.                         } else if (event.type == DnB_press_type) {
  326.  
  327.                             XDeviceButtonEvent *B=(XDeviceButtonEvent *)&event;
  328.  
  329.                             if (B->deviceid == DnB_device_id) {
  330.                                 buttP[B->button-1] = GL_TRUE;/*mrk cur butpres*/
  331.                                 myButtPress = GL_TRUE;   /* set butpres flag */
  332.                             } else
  333.                                 fprintf(stderr,"cable has been disconnected\n");
  334.                         
  335.                         } else if (event.type == DnB_release_type) {
  336.  
  337.                             XDeviceButtonEvent *B=(XDeviceButtonEvent *)&event;
  338.  
  339.                             if (B->deviceid == DnB_device_id) {
  340.                                 buttR[B->button-1] = GL_TRUE;/*mrk cur butrel*/
  341.                                 myButtRelease = GL_TRUE;  /* set butrel flag */
  342.                             } else
  343.                                 fprintf(stderr,"cable has been disconnected\n");
  344.                         }
  345.                     }
  346.                     break;
  347.  
  348.             }  /* end switch (event.type) */
  349.  
  350.         } while (XPending(dpy));   /* end "do { } while".
  351.                                     * XPending() is like qtest()--it only 
  352.                                     * tells you if there're any events 
  353.                                     * presently in the queue.  it does not 
  354.                                     * disturb queue's contents in any way.
  355.                                     */
  356.  
  357.  
  358.     /* On an "Expose" event, redraw the popped, deiconified, or exposed-by-
  359.      *  another-window-being-pushed window.
  360.      */
  361.         if (myExpose) {
  362.             if (myExpose & 2) {
  363.                 Winset(DBglwin);                 /* draw doublebufr'd window*/
  364.                 glViewport(0,  0, xsize-ysize-1, ysize-1);
  365.                 makeDBwin();                  
  366.             }
  367.             if (myExpose & 1) {
  368.                 Winset(SBglwin);                  /*draw singlebufr'd window*/
  369.                 glViewport(0,  0, ysize-1, ysize-1);
  370.                 makeSBwin();                   
  371.                 if (myButtPress) {             /* check to see if button is */
  372.                     for (i=0; i<NumButtons; i++) {   /* still being pressed */
  373.                         if (buttP[i])
  374.                             draw_button(i, SWITCH_ON);/* redraw pressed but */
  375.                     }
  376.                 }
  377.         }
  378.             myExpose = GL_FALSE;             /* reset flag--queue now empty */
  379.         }
  380.  
  381.     /* On a "ConfigureNotify" event, the "GL" window has been moved. 
  382.      */
  383.         if (myConfigure) {
  384.             Winset(DBglwin);                  /* draw the DB win's contents */
  385.             makeDBwin();                   
  386.             Winset(SBglwin);                  /* draw the SB win's contents */
  387.             makeSBwin();                   
  388.             if (myButtPress) {                 /* check to see if button is */
  389.                 for (i=0; i<NumButtons; i++) {       /* still being pressed */
  390.                     if (buttP[i]) 
  391.                         draw_button(i, SWITCH_ON);   /* redraw pressed butt */
  392.                 }
  393.             }
  394.             myConfigure = GL_FALSE;          /* reset flag--queue now empty */
  395.         }
  396.  
  397.     /* For a motion-type event (a dial turning), update the angle values
  398.      * of whichever dials have just be sent to the event queue.
  399.      */
  400.         if (myMotion) {
  401.             Winset(DBglwin);
  402.             for (i=0; i<NumDials; i++) {
  403.                 firstdialtouched = GL_TRUE;      /* at this point a dial is */
  404.                 draw_dial(i);                    /* being turned.           */
  405.                 dialaxis[i] = GL_FALSE;       
  406.             }
  407.             glXSwapBuffers(dpy, glwins[DBglwin]);
  408.             myMotion = GL_FALSE;
  409.         }
  410.  
  411.     /* For a "button press"-type event draw whichever buttons have been
  412.      * pressed since the last time the event queue was drained.
  413.      */
  414.         if (myButtPress) {
  415.             Winset(SBglwin);
  416.             for (i=0; i<NumButtons; i++) {
  417.                 if (buttP[i])
  418.                     draw_button(i, SWITCH_ON); /* draw button being pressed */
  419.             }
  420.         }
  421.  
  422.     /* For a "button release"-type event draw whichever buttons (in "off"
  423.      * position) have been released since last time the event q was drained.
  424.      */
  425.         if (myButtRelease) {
  426.             Winset(SBglwin);
  427.             for (i=0; i<NumButtons; i++) {
  428.                 if (buttR[i])  {
  429.                     draw_button(i, SWITCH_OFF);
  430.                     buttR[i] = GL_FALSE;
  431.                     buttP[i] = GL_FALSE;
  432.                 }
  433.         }
  434.             myButtPress = GL_FALSE;
  435.             myButtRelease = GL_FALSE;
  436.         }
  437.     }
  438. }
  439.  
  440.  
  441.  
  442. #include <Xm/MwmUtil.h>
  443.  
  444.  
  445. static int SBattributeList[] = { None };
  446. static int DBattributeList[] = { GLX_DOUBLEBUFFER,
  447.                                  None };
  448.  
  449. /* WaitForNotify:
  450.  *   used to make sure the MapWindow() calls inside openwindow() occur
  451.  *   beFORE glXMakeCurrent() is invoked so as to avoid a race condition.
  452.  */
  453. static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
  454.     return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
  455. }
  456.  
  457.  
  458. Colormap SBcmap, DBcmap;  /* need both these handles in openwindow & initGL */
  459.  
  460. /*  openwindow -
  461.  *     establish connection to X server, get screen info, specify the
  462.  *     attributes we want the WM to try to provide, and create the GL windows
  463.  */
  464. static void 
  465. openwindow(char *progname) 
  466. {
  467.     int screen_num;                           /* X screen number            */
  468.     long xorig, yorig;                        /* window (upper-left) origin */
  469.     XSizeHints Winhints;                         /* used to fix window size */
  470.     Atom atomName;                                   /* used to make parent */
  471.     MotifWmHints mwm;                                /* win non-resizeable  */
  472.     XSetWindowAttributes SBswa, DBswa;        
  473.     XVisualInfo *SBvi, *DBvi;
  474.  
  475.  
  476.  
  477.   /* connect to the X server and get screen info */
  478.     if ((dpy = XOpenDisplay(NULL)) == NULL) {
  479.         fprintf(stderr, "%s: cannot connect to X server %s\n",
  480.                                  progname, XDisplayName(NULL));
  481.         exit(1);
  482.     }
  483.     screen_num = DefaultScreen(dpy);
  484.  
  485.   /* define window (upper-left) origin coords */
  486.     xorig = (DisplayWidth(dpy, screen_num) - xsize) / 2;
  487.     yorig = (DisplayHeight(dpy, screen_num) - ysize) / 2;
  488.  
  489.   /* create top level X window which will be the parent of the 2 GL windows */
  490.     glwins[top] = XCreateSimpleWindow(dpy, RootWindow(dpy, screen_num),
  491.                                     xorig, yorig, xsize, ysize, 0, 0, 0);
  492.     if (!(glwins[top])) {
  493.         fprintf(stderr,"%s: couldn't create \"parent\" X window\n",progname);
  494.         exit(1);
  495.     }
  496.    /* including this call to XSelectInput shud not be necessary, but there's
  497.     * a bug, so we have to resort to this hackaround.
  498.     */
  499.     XSelectInput(dpy, glwins[top], StructureNotifyMask | ButtonPressMask |
  500.                                      ButtonReleaseMask | ExposureMask);
  501.  
  502.   /* define string that will show up in the window title bar (and icon) */
  503.     XStoreName(dpy, glwins[top], "dial & button box confidence test");
  504.  
  505.   /* specify the values for the Window Size Hints we want to enforce:  this 
  506.    *  window can be moved but we don't want to allow any resizing to occur. 
  507.    */
  508.     Winhints.x = xorig;         /* specify desired upper-left corner origin */
  509.     Winhints.y = yorig;         /* of window so prog will place itself      */
  510.     Winhints.width  = xsize;          /* specify desired x/y size of window */
  511.     Winhints.height = ysize;
  512.     Winhints.min_width = xsize;               /* set min and max width and  */
  513.     Winhints.max_width = xsize;               /* height to be the same so   */
  514.     Winhints.min_height = ysize;              /* window cannot be resized   */
  515.     Winhints.max_height = ysize;
  516.     Winhints.flags = USPosition|USSize|PMaxSize|PMinSize; /* set pert. flgs */
  517.     XSetNormalHints(dpy, glwins[top], &Winhints);
  518.  
  519.   /* the following, culminating with XChangeProperty, enables us to remove
  520.    *  the "[re]size" menu item from the default window menu, the "maximize"
  521.    *  control button on the window frame, and the resize handle cursors 
  522.    *  that are normally available when the cursor moves into the proximity 
  523.    *  of any of the window's 4 corners.
  524.    */
  525.     mwm.flags = MWM_HINTS_FUNCTIONS;
  526.    /* remove the decorations from the function list we don't want to have */
  527.     mwm.functions = MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE;
  528.     mwm.decorations = 0;
  529.     mwm.input_mode = 0;
  530.     atomName = XInternAtom(dpy, _XA_MOTIF_WM_HINTS, False);
  531.     XChangeProperty(dpy, glwins[top], atomName, atomName, sizeof(long)*8, 
  532.             PropModeReplace, (unsigned char*) &mwm, sizeof(mwm)/sizeof(long));
  533.  
  534.   /* now define and create the single and doublebuffer rendering windows */
  535.  
  536.   /* first, get the appropriate visuals */
  537.     SBvi = glXChooseVisual(dpy, screen_num, SBattributeList);
  538.     if (SBvi == NULL) {
  539.         fprintf(stderr,"cudn't create a singlebuffered visual (???)\n");
  540.         clean_exit();
  541.     }
  542.     DBvi = glXChooseVisual(dpy, screen_num, DBattributeList);
  543.     if (SBvi == NULL) {
  544.         fprintf(stderr,"cudn't create a doublebuffered visual (???)\n");
  545.         clean_exit();
  546.     }
  547.  
  548.   /* now create the appropriate  GLX contexts */
  549.     glcontexts[SBglwin] = glXCreateContext(dpy, SBvi, NULL, GL_FALSE);
  550.     glcontexts[DBglwin] = glXCreateContext(dpy, DBvi, NULL, GL_FALSE);
  551.  
  552.   /* now create the two colormaps */
  553.     SBcmap = XCreateColormap(dpy, RootWindow(dpy, SBvi->screen),
  554.                            SBvi->visual, AllocAll);
  555.     DBcmap = XCreateColormap(dpy, RootWindow(dpy, DBvi->screen),
  556.                            DBvi->visual, AllocAll);
  557.  
  558.   /* now create both windows */
  559.     SBswa.colormap = SBcmap;
  560.     SBswa.border_pixel = 0;
  561.     SBswa.event_mask = StructureNotifyMask | ButtonReleaseMask | ExposureMask |
  562.          Button1MotionMask | KeyPressMask;    /* express interest in events */
  563.     glwins[SBglwin] = XCreateWindow(dpy, glwins[top],
  564.                   0, 0, ysize, ysize,
  565.                       0, SBvi->depth, InputOutput, SBvi->visual,
  566.                       CWBorderPixel|CWColormap|CWEventMask, &SBswa);
  567.     DBswa.colormap = DBcmap;
  568.     DBswa.border_pixel = 0;
  569.     DBswa.event_mask = StructureNotifyMask | ButtonReleaseMask | ExposureMask |
  570.          Button1MotionMask | KeyPressMask;    /* express interest in events */
  571.     glwins[DBglwin] = XCreateWindow(dpy, glwins[top],
  572.                   ysize, 0, xsize, ysize,
  573.                       0, DBvi->depth, InputOutput, DBvi->visual,
  574.                       CWBorderPixel|CWColormap|CWEventMask, &DBswa);
  575.  
  576.   /* now map the windows--they won't be made visible until we process the 
  577.    *  expose events up in main's infinite loop which these calls will 
  578.    *  generate.  we use XIfEvent to prevent race conditions for the children
  579.    *  SB and DB windows (which are the only ones we'll ever invoke in 
  580.    *  glXMakeCurrent())
  581.    */
  582.     XMapWindow(dpy, glwins[DBglwin]);
  583.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[DBglwin]);
  584.     XMapWindow(dpy, glwins[SBglwin]);
  585.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[SBglwin]);
  586.     XMapWindow(dpy, glwins[top]);           /* and finally map the X parent */
  587.  
  588.   /* for starters, connect the context to the singlebuffer window */
  589.     if (!glXMakeCurrent(dpy, glwins[SBglwin], glcontexts[SBglwin]) == GL_TRUE) {
  590.         fprintf(stderr, "error w/glXMakeCurrent:  cudn't set");
  591.         fprintf(stderr, " context to the singlebuffered GL window\n");
  592.         exit(-1);
  593.     }
  594.  
  595.   /* ensure the GL colormap is installed for this app */
  596.     XSetWMColormapWindows(dpy, glwins[top], glwins, WINMAX);
  597.  
  598.   /* express interest in WM being able to kill this app */
  599.     if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", True)) != None)
  600.         XSetWMProtocols(dpy, glwins[top], &del_atom, 1);
  601.  
  602.     glFlush();
  603. }
  604.  
  605.  
  606. static void 
  607. initGL(void)
  608. {
  609.     XColor colorstruct;
  610.  
  611.     colorstruct.pixel = BLACK;                   /* define colors we'll use */
  612.     colorstruct.red   = 0;
  613.     colorstruct.green = 0;
  614.     colorstruct.blue  = 0;
  615.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  616.     XStoreColor(dpy, SBcmap, &colorstruct);
  617.     XStoreColor(dpy, DBcmap, &colorstruct);
  618.     colorstruct.pixel = RED;
  619.     colorstruct.red   = 65535;
  620.     colorstruct.green = 0;
  621.     colorstruct.blue  = 0;
  622.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  623.     XStoreColor(dpy, SBcmap, &colorstruct);
  624.     XStoreColor(dpy, DBcmap, &colorstruct);
  625.     colorstruct.pixel = BLUE;
  626.     colorstruct.red   = 0;
  627.     colorstruct.green = 0;
  628.     colorstruct.blue  = 65535;
  629.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  630.     XStoreColor(dpy, SBcmap, &colorstruct);
  631.     XStoreColor(dpy, DBcmap, &colorstruct);
  632.     colorstruct.pixel = WHITE;
  633.     colorstruct.red   = 65535;
  634.     colorstruct.green = 65535;
  635.     colorstruct.blue  = 65535;
  636.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  637.     XStoreColor(dpy, SBcmap, &colorstruct);
  638.     XStoreColor(dpy, DBcmap, &colorstruct);
  639.  
  640.     qobj = gluNewQuadric();                /* define basics for dial drawing */
  641.     gluQuadricDrawStyle(qobj, GLU_FILL);
  642.  
  643.     Winset(DBglwin);                /* define DB win's orthographic project */
  644.     glLoadIdentity();
  645.     gluOrtho2D(0.0,  30.0,  0.0,  40.0);
  646.  
  647.     Winset(SBglwin);                /* define SB win's orthographic project */
  648.     glLoadIdentity();
  649.     gluOrtho2D(0.0,  40.0,  0.0,  40.0);
  650.  
  651.     glFlush();
  652. }
  653.  
  654.  
  655. /*  setupdevs - 
  656.  *
  657.  *   establish a live connection to the dial+buttons device.
  658.  *
  659.  *   leverages off the "X11 Input Extension Library Specification" 
  660.  *   document (you *shud* be able to locate the on-line public access 
  661.  *   directory which contains all the files to print hard-copy of this
  662.  *   document under .../mit/doc/extensions/xinput).  refer to 
  663.  *   /usr/include/X11/extensions/{XI.h, XInput.h} for structures accessed.
  664.  */
  665. static void 
  666. setupdevs(int buttP[NumButtons], int buttR[NumButtons], int dialaxis[NumDials])
  667. {
  668.     int i, ndevices;
  669.     XDevice *DnB_device;
  670.     XDeviceInfoPtr lp, list;
  671.     int num_ext_event_classes;
  672.     XEventClass ListOfEventClass[3];
  673.     int DnB_press_class, DnB_release_class, DnB_motion_class;
  674.  
  675.  
  676.     /* get a ptr to the list of all currently defined input devices */
  677.     list = (XDeviceInfoPtr) XListInputDevices(dpy, &ndevices); 
  678.     if (!list) {
  679.         fprintf(stderr,"XlistInputDevices failed to generate a devices list\n");
  680.         exit(1);
  681.     }
  682.  
  683.    /* check out the /usr/people/4Dgifts/examples/devices/input/X/Xlist.c
  684.     *  program (which gets compiled into "xlist").  running it will list
  685.     *  all the currently available input devices on the machine xlist is
  686.     *  run on.  there is a LOT that should be studied in the "input" subtree.
  687.     */
  688.     for (lp=list, i=0; i<ndevices; lp++, i++) {
  689.         if (lp->use==IsXExtensionDevice && strcmp(lp->name,"dial+buttons")==0) {
  690.             break;  /* found the right one--now save the ptr (lp) to it */
  691.         }
  692.     }
  693.     if (i == ndevices) {
  694.         fprintf(stderr, "\"dial+buttons\" device was not found\n");
  695.         exit(1);
  696.     }
  697.     DnB_device = XOpenDevice(dpy, lp->id);           /* open the DnB device */
  698.     if (!DnB_device) {
  699.         fprintf(stderr, "XOpenDevice failed for \"dial+buttons\" device\n");
  700.         exit(1);
  701.     }
  702.     DnB_device_id = DnB_device->device_id;        /* save out the device id */
  703.  
  704.    /* the following 3 macros determine the given event's type and class.  
  705.     *  each macro is passed the structure that describes the device from 
  706.     *  which input is desired.
  707.     */
  708.     DeviceButtonPress(DnB_device, DnB_press_type, DnB_press_class);
  709.     DeviceButtonRelease(DnB_device, DnB_release_type, DnB_release_class);
  710.     DeviceMotionNotify(DnB_device, DnB_motion_type, DnB_motion_class);
  711.  
  712.     ListOfEventClass[0] = DnB_press_class;
  713.     ListOfEventClass[1] = DnB_release_class;
  714.     ListOfEventClass[2] = DnB_motion_class;
  715.  
  716.     num_ext_event_classes = 3;
  717.  
  718.    /* XSelectExtensionEvent requests the server to send events that match
  719.     *  the events and devices described by the event list and that come
  720.     *  from the requested window.
  721.     * first, we'll put in our button press/release requests for the
  722.     *  single-buffered (buttons and text) GL window.
  723.     */
  724.     XSelectExtensionEvent(dpy, glwins[SBglwin],
  725.                           ListOfEventClass, num_ext_event_classes);
  726.  
  727.    /* now we'll request the Motion events for the double-buffered (dials)
  728.     *  GL window.  
  729.     * note that in each of these two invocations of XSelectExtensionEvent,
  730.     *  we're selecting all three event classes in each window.  this is 
  731.     *  because, no matter where the cursor is inside the window containing 
  732.     *  both the single and double -buffered GL windows, we want to process 
  733.     *  dial or button events, whether or not the mouse is physically 
  734.     *  present in either of these two respective windows.
  735.     */
  736.     XSelectExtensionEvent(dpy, glwins[DBglwin],
  737.                           ListOfEventClass, num_ext_event_classes);
  738.  
  739.    /* initialize all the buttons and dial "markers" to indicate none have 
  740.     *  yet been pressed or moved.
  741.     */
  742.     for (i=0; i<NumButtons; i++) {
  743.         buttP[i]=GL_FALSE;
  744.         buttR[i]=GL_FALSE;
  745.     }
  746.     for (i=0; i<NumDials; i++) {
  747.         dialaxis[i]=GL_FALSE;
  748.     }
  749. }
  750.  
  751.  
  752.  
  753. char *typeToName[] = {
  754.     "Colormap single buffer",
  755.     "Colormap double buffer",
  756. };
  757.  
  758.  
  759. /* A little helper wrapper for glXMakeCurrent.
  760.  *  passes the index to jointly access the windows and contexts array and 
  761.  *  checks the return value.  This makes the call to begin GL drawing a
  762.  *  little simpler.  Building in such automatic error checking is always a
  763.  *  "smooth move" (*not* like the cancerously-mutant human with the enlarged
  764.  *  proboscis plastered all over the place urging people to be likewise 
  765.  *  smoothly cancerous and hardly cool).
  766.  */
  767. void 
  768. Winset(int type)
  769. {
  770.     int rv;
  771.  
  772.     XSync(dpy,GL_FALSE);
  773.     rv = glXMakeCurrent(dpy, glwins[type], glcontexts[type]);
  774.     if (rv == GL_FALSE) {
  775.         fprintf(stderr, "glXMakeCurrent failed for a %s-type window\n",
  776.                                             typeToName[type]);
  777.         exit(-1);
  778.     }
  779. }
  780.  
  781.  
  782.  
  783. /*  makeSBwin -- Draw the buttons and text in the singlebuffer'd GL window
  784.  */
  785. static void 
  786. makeSBwin(void)
  787. {
  788.     int  i;
  789.  
  790.  
  791.     glClearIndex(WHITE);                             /* clear the window    */
  792.     glClear(GL_COLOR_BUFFER_BIT);                    /* background to white */
  793.  
  794.     for (i=0; i<NumButtons; i++)
  795.         draw_button(i, SWITCH_OFF);                     /* draw the buttons */
  796.  
  797.     glIndexi(BLACK);
  798.     glRasterPos2i(2, 1);              /* "charstr()" replacement draws text */
  799.     printString("Use left mouse button to quit.");  /* in lower left corner */
  800. }
  801.  
  802.  
  803. /*  makeDBwin -- Draw the dials in the doublebuffer'd GL window
  804.  */
  805. static void 
  806. makeDBwin(void)
  807. {
  808.     int  i;
  809.     
  810.  
  811.     glClearIndex(WHITE);                             /* clear the window    */
  812.     glClear(GL_COLOR_BUFFER_BIT);                    /* background to white */
  813.  
  814.     for (i=0; i<NumDials; i++)
  815.         draw_dial(i);                                 /* and draw the dials */
  816.  
  817.     glXSwapBuffers(dpy, glwins[DBglwin]);
  818.  
  819.     if (myExpose) {                                /* if an Expose is being */
  820.         glClearIndex(WHITE);                       /* processed, go ahead & */
  821.         glClear(GL_COLOR_BUFFER_BIT);              /* draw the whole new    */
  822.         for (i=0; i<NumDials; i++)                 /* back-buffer with what */
  823.             draw_dial(i);                          /* we just swaped into   */
  824.         glXSwapBuffers(dpy, glwins[DBglwin]);      /* the present front     */
  825.     }                                              /* (visible) buffer      */
  826. }
  827.  
  828.  
  829.  
  830. /*  draw_dial --  Draw dial number dia[0...7]
  831.  */
  832. static void 
  833. draw_dial(int dialnum)
  834. {
  835.     DB_DIAL *dptr = &dia[dialnum];
  836.     float   xcenter, ycenter;
  837.     int     angle;
  838.     char    str[80];
  839.  
  840.  
  841.     xcenter = dptr->xcntr;
  842.     ycenter = dptr->ycntr;
  843.     angle   = dptr->angle;
  844.  
  845.     if ((firstdialtouched) &&             /* when we know a dial has        */
  846.         (dialaxis[dialnum])) {            /* actually been turned, update   */
  847.         Winset(SBglwin);                  /* the text area that resides in  */
  848.         glIndexi(WHITE);                  /* the singlebuffer'd GL window   */
  849.         glRecti(2, 2, 24, 5);                  
  850.         sprintf(str,"Dial %d intensity is %d",dialnum,angle);
  851.         glIndexi(BLUE);
  852.         glRasterPos2i(2, 3);
  853.         printString(str);
  854.         glFlush();
  855.     }
  856.  
  857.     Winset(DBglwin);                       /* now switch cur gfx context to */
  858.     angle   = (dptr->angle * 3600) >> 10;  /* doublebuffer'd window & update*/
  859.     glIndexi(WHITE);                       /* dial currently being turned:  */
  860.     glPushMatrix();                         /* FIRST: "white-out" the entire*/
  861.       glTranslatef(xcenter,  ycenter, 0.0); 
  862.       gluDisk(qobj, 0.0, 2.5, 32, 1); 
  863.     glPopMatrix(); 
  864.     glIndexi(BLACK);
  865.     glPushMatrix(); 
  866.       glTranslatef(xcenter,  ycenter, 0.0); 
  867.       gluDisk(qobj, 2.4, 2.5, 32, 1);
  868.     glPopMatrix();
  869.     glIndexi(RED);
  870.     glPushMatrix(); 
  871.       glTranslatef(xcenter,  ycenter, 0.0); 
  872.       gluPartialDisk(qobj, 0.0, 2.5, 32, 1, 0.0, angle*0.1); 
  873.     glPopMatrix();
  874.  
  875.     glFlush();
  876. }
  877.  
  878.  
  879. #define BSIZE           3.0
  880.  
  881. /*  draw_button --  Draw button number buttno in its on or off state
  882.  */
  883. static void 
  884. draw_button(int buttno, int state)
  885. {
  886.     DB_BUTTON      *bptr = &but[buttno];
  887.     unsigned long  button_led = 0;
  888.     float          xpos, ypos;
  889.     char           str[80];
  890.     
  891.  
  892.     xpos = bptr->xpos;
  893.     ypos = bptr->ypos;
  894.  
  895.     if (state == SWITCH_ON) {
  896.         glIndexi(WHITE);                  /* white out the text area to     */
  897.         glRecti(2, 2, 24, 5);             /* prepare for new button press   */
  898.  
  899.         sprintf(str, "Button %d is being pressed", buttno);
  900.         glIndexi(BLUE);  
  901.         glRasterPos2i(2, 3);              /* update text string in bottom-  */
  902.         printString(str);                 /* -left corner of the SB window  */
  903.  
  904.        /* now draw button being pressed in RED--have to resort to a hack here: 
  905.         * in older IrisGL, cmds like rect rendered "bloated" polygons where an 
  906.     * outline would be made around the area to be filled and then the 
  907.     * outline would be drawn over as well.  In the current point-sampled
  908.     * approach, two adjacent sides do NOT get drawn over, thus the need to
  909.     * do a WHITE glRecti first with each edge being expanded away from the
  910.     * center by 1 pixel.  (to observe the difference, comment out the next
  911.     * two lines and see the differing results.)
  912.     */
  913.         glIndexi(WHITE);             
  914.         glRecti(xpos-1,  ypos-1,  xpos+BSIZE+1,  ypos+BSIZE+1); 
  915.         glIndexi(RED);                      
  916.         glRecti(xpos,  ypos,  xpos+BSIZE,  ypos+BSIZE);
  917.  
  918.     } else {
  919.         glIndexi(WHITE);
  920.         glRecti(2, 2, 24, 5);              /* white out area where text was */
  921.         glRecti(xpos, ypos, xpos+BSIZE, ypos+BSIZE);
  922.         glPolygonMode(GL_FRONT, GL_LINE);  /* now draw in "outline" mode to */
  923.         glIndexi(BLACK);                   /* draw an unpressed button, and */
  924.         glRecti(xpos, ypos, xpos+BSIZE, ypos+BSIZE); /* when we're finished */
  925.         glPolygonMode(GL_FRONT, GL_FILL);  /* return to "fill"/default mode */
  926.     }
  927.  
  928. }
  929.  
  930.  
  931. /* makeRasterFont() and printString() are lifted out of font.c (lives in 
  932.  * this same directory) as a replacement to the IrisGL charstr() function.
  933.  */
  934. GLuint base;
  935.  
  936. static void makeRasterFont(Display **dpy)
  937. {
  938.     XFontStruct *fontInfo;
  939.     Font id;
  940.     unsigned int first, last;
  941.  
  942.     fontInfo = XLoadQueryFont(*dpy,
  943.       "-sgi-screen-bold-r-normal--15-150-72-72-m-90-iso8859-1");
  944.     if (fontInfo == NULL) {
  945.         printf("no font found\n");
  946.         exit(0);
  947.     }
  948.     id = fontInfo->fid;
  949.     first = fontInfo->min_char_or_byte2;
  950.     last = fontInfo->max_char_or_byte2;
  951.     base = glGenLists(last+1);
  952.     if (base == 0) {
  953.         printf("out of display lists\n");
  954.         exit(0);
  955.     }
  956.     glXUseXFont(id, first, last-first+1, base+first);
  957. }
  958.  
  959.  
  960. static void printString(char *s)
  961. {
  962.     glPushAttrib(GL_LIST_BIT);
  963.     glListBase(base);
  964.     glCallLists(strlen(s), GL_UNSIGNED_BYTE, (unsigned char *)s);
  965.     glPopAttrib();
  966. }
  967.  
  968.  
  969. /*  clean_exit  --  Clean up before exiting
  970.  */
  971. static void 
  972. clean_exit(void)
  973. {
  974.     XCloseDisplay(dpy);
  975.     exit(0);
  976. }
  977.